#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
向日葵Git仓库管理 v2.4 (增强版)
支持GitHub、Gitee、GitCode等多平台的Git仓库管理
优化SSH认证和空仓库处理
"""

import os
import sys
import json
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext, simpledialog
import subprocess
import threading
from datetime import datetime
from pathlib import Path
import re
from urllib.parse import urlparse, urlunparse
import shutil

# 尝试导入GitPython
try:
    from git import Repo, GitCommandError
    import git
except ImportError:
    print("正在安装GitPython...")
    subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'GitPython'])
    from git import Repo, GitCommandError
    import git

class PlatformConfig:
    """平台配置类"""
    
    PLATFORMS = ['GitHub', 'Gitee', 'GitCode', 'GitLab', '其他']
    
    DEFAULT_PROXIES = {
        'GitHub': 'https://gh-proxy.com',
        'Gitee': '',
        'GitCode': '',
        'GitLab': '',
        '其他': ''
    }
    
    def __init__(self):
        self.configs = {}
        for platform in self.PLATFORMS:
            self.configs[platform] = {
                'username': '',
                'password': '',
                'email': '',
                'auth_type': 'password',
                'ssh_key_path': '',
                'use_proxy': platform == 'GitHub',
                'proxy_url': self.DEFAULT_PROXIES[platform]
            }
    
    def get_config(self, platform):
        return self.configs.get(platform, {})
    
    def set_config(self, platform, config):
        if platform in self.configs:
            self.configs[platform].update(config)
    
    def to_dict(self):
        return self.configs
    
    def from_dict(self, data):
        for platform in self.PLATFORMS:
            if platform in data:
                self.configs[platform].update(data[platform])

class RepoSyncManager:
    """仓库同步管理器"""
    
    def __init__(self):
        self.sync_groups = {}
        self.repo_to_group = {}
    
    def create_sync_group(self, main_repo, subordinate_repos):
        group_id = f"group_{len(self.sync_groups) + 1}"
        self.sync_groups[group_id] = {
            'main': main_repo,
            'subordinates': subordinate_repos
        }
        
        self.repo_to_group[main_repo] = group_id
        for repo in subordinate_repos:
            self.repo_to_group[repo] = group_id
        
        return group_id
    
    def remove_sync_group(self, group_id):
        if group_id in self.sync_groups:
            group = self.sync_groups[group_id]
            if group['main'] in self.repo_to_group:
                del self.repo_to_group[group['main']]
            for repo in group['subordinates']:
                if repo in self.repo_to_group:
                    del self.repo_to_group[repo]
            del self.sync_groups[group_id]
    
    def get_repo_role(self, repo_name):
        if repo_name not in self.repo_to_group:
            return None
        
        group_id = self.repo_to_group[repo_name]
        group = self.sync_groups[group_id]
        
        if group['main'] == repo_name:
            return 'main'
        elif repo_name in group['subordinates']:
            return 'subordinate'
        return None
    
    def get_subordinates(self, main_repo):
        if main_repo not in self.repo_to_group:
            return []
        
        group_id = self.repo_to_group[main_repo]
        group = self.sync_groups[group_id]
        
        if group['main'] == main_repo:
            return group['subordinates']
        return []
    
    def to_dict(self):
        return {
            'sync_groups': self.sync_groups,
            'repo_to_group': self.repo_to_group
        }
    
    def from_dict(self, data):
        self.sync_groups = data.get('sync_groups', {})
        self.repo_to_group = data.get('repo_to_group', {})

class GitRepoManager:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("🌻 向日葵Git仓库管理 v2.4")
        self.root.geometry("1550x1050")
        self.root.minsize(1200, 700)
        
        # 设置应用图标和颜色主题
        self.setup_theme()
        
        # 设置样式
        self.setup_styles()
        
        # 配置文件路径
        self.config_dir = Path.home() / ".sunflower_git_manager"
        self.config_dir.mkdir(exist_ok=True)
        self.config_file = self.config_dir / "config.json"
        
        # 初始化配置
        self.platform_config = PlatformConfig()
        self.sync_manager = RepoSyncManager()
        self.repos_info = {}
        self.current_repo_path = None
        self.repos_folder = None
        
        # 初始化日志组件
        self.log_text = None
        
        # 加载配置
        self.load_config()
        
        # 创建UI
        self.setup_ui()
        
        # 加载仓库列表
        if self.repos_folder:
            self.refresh_repo_list()
        
        # 居中主窗口
        self.center_window()
    
    def setup_theme(self):
        """设置主题颜色"""
        self.root.configure(bg='#f5f5f5')
        
        # 向日葵主题色
        self.colors = {
            'primary': '#FFD700',      # 向日葵金色
            'secondary': '#FFA500',    # 橙色
            'success': '#32CD32',      # 成功绿
            'info': '#4169E1',         # 信息蓝
            'warning': '#FF8C00',      # 警告橙
            'danger': '#DC143C',       # 危险红
            'bg': '#FFFACD',          # 淡黄背景
            'text': '#333333'          # 文本色
        }
    
    def setup_styles(self):
        """设置全局样式 - 向日葵主题"""
        style = ttk.Style()
        style.theme_use('clam')
        
        # 配置按钮样式 - 向日葵色调
        style.configure('Action.TButton', 
                       padding=(12, 10),
                       relief='raised',
                       font=('Microsoft YaHei', 10, 'bold'),
                       background='#FFD700',
                       foreground='#333333')
        style.map('Action.TButton',
                 background=[('active', '#FFA500')])
        
        # 配置次要按钮样式
        style.configure('Secondary.TButton',
                       padding=(10, 8),
                       font=('Microsoft YaHei', 10))
        
        # 配置标签框样式
        style.configure('Custom.TLabelframe', 
                       background='#FFFACD',
                       relief='solid',
                       borderwidth=2)
        style.configure('Custom.TLabelframe.Label', 
                       font=('Microsoft YaHei', 11, 'bold'),
                       background='#FFFACD',
                       foreground='#333333')
        
        # 配置标签样式
        style.configure('Title.TLabel',
                       font=('Microsoft YaHei', 14, 'bold'),
                       foreground='#FF8C00')
        
        style.configure('Heading.TLabel',
                       font=('Microsoft YaHei', 12, 'bold'),
                       foreground='#333333')
    
    def is_ssh_url(self, url):
        return url.startswith('git@') or url.startswith('ssh://')
    
    def is_https_url(self, url):
        return url.startswith('http://') or url.startswith('https://')
    
    def to_ssh_url(self, url, platform):
        if not self.is_https_url(url):
            return url
        
        parsed = urlparse(url)
        netloc = parsed.netloc
        path = parsed.path.lstrip('/')
        
        if path.endswith('.git'):
            path = path[:-4]
        
        host_match = re.match(r'(?:www\.)?(.+)', netloc)
        if host_match:
            host = host_match.group(1)
            return f"git@{host}:{path}.git"
        
        return url
    
    def process_remote_url(self, url, platform, config):
        if not url:
            return url
        
        if config.get('auth_type') == 'ssh':
            url = self.to_ssh_url(url, platform)
            self.log(f"转换为SSH URL: {url}", level="INFO")
        
        if self.is_ssh_url(url):
            self.log(f"使用SSH协议: {url}", level="INFO")
            return url
        
        if self.is_https_url(url):
            if config.get('auth_type') == 'password' and config.get('password'):
                url = self.build_auth_url(url, platform, config)
            
            if config.get('use_proxy') and config.get('proxy_url'):
                proxy = config['proxy_url']
                
                if 'gh-proxy.com' in proxy or 'ghproxy' in proxy:
                    if not url.startswith(proxy):
                        url = f"{proxy.rstrip('/')}/{url}"
                        self.log(f"使用代理前缀: {proxy}", level="INFO")
                else:
                    if not proxy.startswith('http'):
                        proxy = f"http://{proxy}"
                    os.environ['HTTP_PROXY'] = proxy
                    os.environ['HTTPS_PROXY'] = proxy
                    self.log(f"设置环境变量代理: {proxy}", level="INFO")
        
        return url
    
    def setup_git_ssh(self, config):
        """设置SSH认证 - 修复使用私钥而非公钥"""
        if config.get('auth_type') == 'ssh' and config.get('ssh_key_path'):
            ssh_key = config['ssh_key_path']
            
            # 如果用户错误地选择了公钥文件，自动修正为私钥
            if ssh_key.endswith('.pub'):
                ssh_key = ssh_key[:-4]
                self.log(f"自动修正：使用私钥文件 {ssh_key}", level="WARNING")
            
            if os.path.exists(ssh_key):
                # Windows下使用引号处理路径中的空格
                if sys.platform == 'win32':
                    ssh_command = f'ssh -i "{ssh_key}" -o StrictHostKeyChecking=no'
                else:
                    ssh_command = f'ssh -i {ssh_key} -o StrictHostKeyChecking=no'
                
                os.environ['GIT_SSH_COMMAND'] = ssh_command
                self.log(f"已配置SSH私钥: {ssh_key}", level="INFO")
                return True
            else:
                self.log(f"SSH私钥文件不存在: {ssh_key}", level="ERROR")
                self.log("提示：请确保选择私钥文件（如id_ed25519），而不是公钥文件（.pub）", level="WARNING")
                return False
        return True
    
    def clear_proxy_env(self):
        for key in ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy', 'GIT_SSH_COMMAND']:
            if key in os.environ:
                del os.environ[key]
    
    def center_window(self, window=None):
        if window is None:
            window = self.root
        
        window.update_idletasks()
        width = window.winfo_width()
        height = window.winfo_height()
        x = (window.winfo_screenwidth() // 2) - (width // 2)
        y = (window.winfo_screenheight() // 2) - (height // 2)
        window.geometry(f'{width}x{height}+{x}+{y}')
    
    def setup_ui(self):
        """设置用户界面 - 向日葵主题优化版"""
        # 主容器
        main_container = ttk.Frame(self.root, style='Custom.TFrame')
        main_container.pack(fill=tk.BOTH, expand=True, padx=15, pady=10)
        
        # ========== 标题栏 ==========
        title_frame = ttk.Frame(main_container)
        title_frame.pack(fill=tk.X, pady=(0, 10))
        
        title_label = ttk.Label(title_frame, 
                               text="🌻 向日葵Git仓库管理系统", 
                               style='Title.TLabel')
        title_label.pack(side=tk.LEFT)
        
        # ========== 顶部工具栏 ==========
        toolbar_frame = ttk.Frame(main_container)
        toolbar_frame.pack(fill=tk.X, pady=(0, 15))
        
        # 工具栏按钮 - 使用向日葵配色
        buttons = [
            ("📁 选择文件夹", self.select_repos_folder),
            ("🔄 刷新列表", self.refresh_repo_list),
            ("⚙️ 平台配置", self.open_platform_settings),
            ("🔗 同步配置", self.open_sync_settings),
            ("➕ 克隆仓库", self.clone_repository),
            ("📤 批量操作", self.batch_operations),
            ("❓ 使用帮助", self.show_help)
        ]
        
        for i, (text, command) in enumerate(buttons):
            btn = ttk.Button(toolbar_frame, text=text, command=command, style='Action.TButton')
            btn.grid(row=0, column=i, padx=5, sticky='ew')
        
        # 配置列权重使按钮均匀分布
        for i in range(len(buttons)):
            toolbar_frame.columnconfigure(i, weight=1)
        
        # ========== 主内容区域 ==========
        content_frame = ttk.Frame(main_container)
        content_frame.pack(fill=tk.BOTH, expand=True)
        
        # 配置网格权重
        content_frame.columnconfigure(0, weight=2)
        content_frame.columnconfigure(1, weight=3)
        content_frame.columnconfigure(2, weight=2)
        content_frame.rowconfigure(0, weight=1)
        
        # ========== 左侧仓库列表 ==========
        left_frame = ttk.LabelFrame(content_frame, text="📂 仓库列表", 
                                   padding="15", style='Custom.TLabelframe')
        left_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
        
        # 搜索框
        search_frame = ttk.Frame(left_frame)
        search_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(search_frame, text="🔍 搜索:", font=('Microsoft YaHei', 10)).pack(side=tk.LEFT, padx=(0, 5))
        self.search_var = tk.StringVar()
        self.search_var.trace('w', self.filter_repos)
        search_entry = ttk.Entry(search_frame, textvariable=self.search_var, font=('Microsoft YaHei', 10))
        search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        # 仓库列表
        list_frame = ttk.Frame(left_frame)
        list_frame.pack(fill=tk.BOTH, expand=True)
        
        self.repo_listbox = tk.Listbox(list_frame, 
                                       font=('Microsoft YaHei', 10),
                                       selectmode=tk.SINGLE,
                                       activestyle='dotbox',
                                       selectbackground='#FFA500',
                                       selectforeground='white',
                                       bg='#FFFFF0')
        self.repo_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.repo_listbox.bind('<<ListboxSelect>>', self.on_repo_select)
        self.repo_listbox.bind('<Double-Button-1>', self.open_repo_folder)
        
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.repo_listbox.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.repo_listbox.yview)
        
        # ========== 中间信息区域 ==========
        middle_frame = ttk.LabelFrame(content_frame, text="📊 仓库信息", 
                                     padding="15", style='Custom.TLabelframe')
        middle_frame.grid(row=0, column=1, sticky="nsew", padx=10)
        
        # 仓库基本信息 - 优化布局
        info_frame = ttk.Frame(middle_frame)
        info_frame.pack(fill=tk.X, pady=(0, 15))
        
        info_labels = [
            ("仓库名称:", self.create_var('repo_name'), 'bold'),
            ("平台:", self.create_var('platform_info'), 'normal'),
            ("当前分支:", self.create_var('branch_info'), 'normal'),
            ("同步角色:", self.create_var('sync_role', "无"), 'bold'),
            ("远程URL:", self.create_var('remote_url'), 'normal'),
            ("认证方式:", self.create_var('auth_type'), 'normal')
        ]
        
        for i, (label_text, var, weight) in enumerate(info_labels):
            label = ttk.Label(info_frame, text=label_text, font=('Microsoft YaHei', 10, 'bold'))
            label.grid(row=i, column=0, sticky=tk.W, pady=5, padx=(0, 10))
            
            if label_text == "远程URL:":
                entry = ttk.Entry(info_frame, textvariable=var, state='readonly', 
                                 font=('Consolas', 10))
                entry.grid(row=i, column=1, sticky=(tk.W, tk.E), pady=5)
            else:
                font_style = ('Microsoft YaHei', 10, weight) if weight == 'bold' else ('Microsoft YaHei', 10)
                value_label = ttk.Label(info_frame, textvariable=var, font=font_style)
                value_label.grid(row=i, column=1, sticky=tk.W, pady=5)
        
        info_frame.columnconfigure(1, weight=1)
        
        # 一键提交区域 - 向日葵配色
        commit_frame = ttk.LabelFrame(middle_frame, text="✏️ 快速提交", 
                                     padding="10", style='Custom.TLabelframe')
        commit_frame.pack(fill=tk.X, pady=(0, 15))
        
        ttk.Label(commit_frame, text="提交信息:", font=('Microsoft YaHei', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        
        self.commit_message = scrolledtext.ScrolledText(commit_frame, 
                                                        width=50, 
                                                        height=3,
                                                        font=('Microsoft YaHei', 10),
                                                        wrap=tk.WORD,
                                                        bg='#FFFFF0')
        self.commit_message.pack(fill=tk.X, pady=(0, 10))
        self.commit_message.insert(1.0, f"Update: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
        
        # 快速提交按钮组
        quick_commit_buttons = ttk.Frame(commit_frame)
        quick_commit_buttons.pack(fill=tk.X)
        
        ttk.Button(quick_commit_buttons, 
                  text="💾 一键提交", 
                  command=self.quick_commit,
                  style='Action.TButton').pack(side=tk.LEFT, padx=(0, 5))
        
        ttk.Button(quick_commit_buttons, 
                  text="📤 提交并推送", 
                  command=self.commit_and_push,
                  style='Action.TButton').pack(side=tk.LEFT, padx=5)
        
        ttk.Button(quick_commit_buttons, 
                  text="🔗 提交并同步", 
                  command=self.commit_and_sync,
                  style='Action.TButton').pack(side=tk.LEFT, padx=5)
        
        # 文件变更列表
        changes_label = ttk.Label(middle_frame, text="📝 文件变更:", 
                                 font=('Microsoft YaHei', 10, 'bold'))
        changes_label.pack(anchor=tk.W, pady=(0, 5))
        
        changes_frame = ttk.Frame(middle_frame)
        changes_frame.pack(fill=tk.BOTH, expand=True)
        
        self.changes_listbox = tk.Listbox(changes_frame, 
                                         selectmode=tk.MULTIPLE,
                                         font=('Consolas', 9),
                                         height=8,
                                         bg='#FFFFF0')
        self.changes_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        changes_scrollbar = ttk.Scrollbar(changes_frame, orient=tk.VERTICAL)
        changes_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.changes_listbox.config(yscrollcommand=changes_scrollbar.set)
        changes_scrollbar.config(command=self.changes_listbox.yview)
        
        # ========== 右侧操作区域 ==========
        right_frame = ttk.LabelFrame(content_frame, text="🎮 快速操作", 
                                    padding="15", style='Custom.TLabelframe')
        right_frame.grid(row=0, column=2, sticky="nsew", padx=(10, 0))
        
        # Git操作按钮 - 优化布局
        operations = [
            ("基本操作", [
                ("🔄 刷新状态", self.refresh_changes),
                ("➕ 添加文件", self.add_files),
                ("⬇️ 拉取更新", self.pull_changes),
                ("⬆️ 推送到远程", self.push_changes),
                ("🔗 推送并同步", self.push_and_sync)
            ]),
            ("分支操作", [
                ("🌿 创建分支", self.create_branch),
                ("🔀 切换分支", self.switch_branch),
                ("🔗 合并分支", self.merge_branch)
            ]),
            ("其他操作", [
                ("📜 查看日志", self.view_log),
                ("🔍 查看差异", self.view_diff),
                ("📂 打开文件夹", self.open_repo_folder)
            ])
        ]
        
        for section_title, buttons in operations:
            section_label = ttk.Label(right_frame, text=section_title, 
                                    style='Heading.TLabel')
            section_label.pack(anchor=tk.W, pady=(10, 8))
            
            for btn_text, btn_command in buttons:
                btn = ttk.Button(right_frame, text=btn_text, 
                               command=btn_command,
                               width=20,
                               style='Secondary.TButton')
                btn.pack(pady=4, fill=tk.X)
            
            if section_title != "其他操作":
                ttk.Separator(right_frame, orient='horizontal').pack(fill=tk.X, pady=10)
        
        # ========== 底部日志区域 ==========
        log_frame = ttk.LabelFrame(main_container, text="📋 操作日志", 
                                  padding="10", style='Custom.TLabelframe')
        log_frame.pack(fill=tk.BOTH, expand=False, pady=(15, 0))
        
        self.log_text = scrolledtext.ScrolledText(log_frame, 
                                                  width=120, 
                                                  height=10,
                                                  font=('Consolas', 9),
                                                  wrap=tk.WORD,
                                                  bg='#FFFFF0')
        self.log_text.pack(fill=tk.BOTH, expand=True)
        
        # 配置日志标签颜色
        self.log_text.tag_config("INFO", foreground="black")
        self.log_text.tag_config("SUCCESS", foreground="green", font=('Consolas', 9, 'bold'))
        self.log_text.tag_config("WARNING", foreground="#FF8C00", font=('Consolas', 9, 'bold'))
        self.log_text.tag_config("ERROR", foreground="red", font=('Consolas', 9, 'bold'))
        self.log_text.tag_config("SYNC", foreground="blue", font=('Consolas', 9, 'bold'))
        
        # 初始日志
        self.log("🌻 向日葵Git仓库管理 v2.4 已启动", level="SUCCESS")
        self.log(f"配置文件位置: {self.config_file}")
    
    def create_var(self, name, default=""):
        """创建并保存变量引用"""
        var = tk.StringVar(value=default)
        setattr(self, f"{name}_var", var)
        return var
    
    def quick_commit(self):
        """一键提交功能"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        commit_msg = self.commit_message.get(1.0, tk.END).strip()
        if not commit_msg or commit_msg.startswith("Update:"):
            # 如果没有自定义消息，弹出输入框
            commit_msg = simpledialog.askstring("提交信息", 
                                               "请输入提交信息:",
                                               initialvalue=f"Update: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
            if not commit_msg:
                return
        
        self.log(f"开始快速提交: {commit_msg}", level="INFO")
        
        try:
            repo = Repo(self.current_repo_path)
            platform = self.get_repo_platform(self.current_repo_path)
            config = self.platform_config.get_config(platform)
            
            # 设置用户信息
            if config.get('username') and config.get('email'):
                with repo.config_writer() as git_config:
                    git_config.set_value('user', 'name', config['username'])
                    git_config.set_value('user', 'email', config['email'])
            
            # 添加所有更改
            repo.git.add('.')
            
            # 检查是否有更改
            if repo.is_dirty() or repo.untracked_files:
                # 提交
                repo.index.commit(commit_msg)
                self.log(f"提交成功: {commit_msg}", level="SUCCESS")
                
                # 重置提交信息框
                self.commit_message.delete(1.0, tk.END)
                self.commit_message.insert(1.0, f"Update: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
                
                # 刷新变更列表
                self.refresh_changes()
                
                messagebox.showinfo("成功", "提交成功！")
            else:
                self.log("没有检测到变更，跳过提交", level="WARNING")
                messagebox.showinfo("提示", "没有检测到需要提交的变更")
                
        except Exception as e:
            self.log(f"提交失败: {str(e)}", level="ERROR")
            messagebox.showerror("错误", f"提交失败：\n{str(e)}")
    
    def commit_and_push(self):
        """提交并推送"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        # 先提交
        self.quick_commit()
        # 再推送
        self.push_changes()
    
    def commit_and_sync(self):
        """提交并同步"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        # 先提交
        self.quick_commit()
        # 再同步
        self.push_and_sync()
    
    def on_repo_select(self, event):
        """选择仓库时的处理 - 修复空仓库处理"""
        selection = self.repo_listbox.curselection()
        if not selection:
            return
            
        index = selection[0]
        repo_display = self.repo_listbox.get(index)
        repo_name = repo_display.split(' [')[0]
        repo_path = os.path.join(self.repos_folder, repo_name)
        
        self.current_repo_path = repo_path
        self.repo_name_var.set(repo_name)
        
        try:
            repo = Repo(repo_path)
            
            platform = self.get_repo_platform(repo_path)
            self.platform_info_var.set(platform)
            
            # 处理空仓库情况
            try:
                branch = repo.active_branch.name if repo.head.ref else "HEAD detached"
            except:
                # 空仓库情况
                branch = "空仓库 (no commits)"
                self.log(f"检测到空仓库: {repo_name}", level="WARNING")
            
            self.branch_info_var.set(branch)
            
            role = self.sync_manager.get_repo_role(repo_name)
            if role == 'main':
                subordinates = self.sync_manager.get_subordinates(repo_name)
                self.sync_role_var.set(f"主仓库 (从: {', '.join(subordinates)})")
            elif role == 'subordinate':
                self.sync_role_var.set("从仓库")
            else:
                self.sync_role_var.set("无")
            
            if repo.remotes:
                remote_url = repo.remotes[0].url
                self.remote_url_var.set(remote_url)
                
                config = self.platform_config.get_config(platform)
                if self.is_ssh_url(remote_url):
                    self.auth_type_var.set("SSH")
                elif config.get('auth_type') == 'ssh':
                    self.auth_type_var.set("SSH (配置)")
                else:
                    self.auth_type_var.set("HTTPS/Token")
            else:
                self.remote_url_var.set("无远程仓库")
                self.auth_type_var.set("未配置")
            
            self.refresh_changes()
            
            self.log(f"已选择仓库: {repo_name} ({platform})", level="SUCCESS")
        except Exception as e:
            self.log(f"加载仓库信息失败: {str(e)}", level="ERROR")
    
    def push_changes(self):
        """推送到远程仓库 - 优化SSH处理"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        self.log("开始推送到远程仓库...", level="INFO")
        
        def push_thread():
            try:
                repo = Repo(self.current_repo_path)
                platform = self.get_repo_platform(self.current_repo_path)
                config = self.platform_config.get_config(platform)
                
                if not repo.remotes:
                    self.root.after(0, lambda: messagebox.showwarning("警告", "没有配置远程仓库"))
                    return
                
                self.clear_proxy_env()
                
                original_url = repo.remotes[0].url
                
                push_url = self.process_remote_url(original_url, platform, config)
                if self.is_ssh_url(push_url):
                    if not self.setup_git_ssh(config):
                        self.root.after(0, lambda: messagebox.showerror("错误", "SSH密钥配置失败\n请确保选择私钥文件（不带.pub后缀）"))
                        return
                
                if push_url != original_url:
                    repo.remotes[0].set_url(push_url)
                
                try:
                    push_info = repo.remotes[0].push()
                    self.log("推送成功！", level="SUCCESS")
                    self.root.after(0, lambda: messagebox.showinfo("成功", "推送成功！"))
                finally:
                    if push_url != original_url:
                        repo.remotes[0].set_url(original_url)
                    self.clear_proxy_env()
                
            except Exception as e:
                error_msg = str(e)
                self.log(f"推送失败: {error_msg}", level="ERROR")
                
                if "403" in error_msg or "Authentication" in error_msg:
                    if self.is_ssh_url(original_url):
                        error_msg += "\n\n可能的原因：\n" \
                                    "1. SSH私钥未正确配置（请选择不带.pub的私钥文件）\n" \
                                    "2. 公钥未添加到GitHub/GitCode等平台\n" \
                                    "3. 没有推送权限"
                    elif platform == 'GitHub':
                        error_msg += "\n\n可能的原因：\n" \
                                    "1. Token权限不足或已过期\n" \
                                    "2. 需要repo权限\n" \
                                    "3. 请在平台配置中检查Token"
                    else:
                        error_msg += "\n\n请检查：\n1. 用户名和密码是否正确\n2. 是否有推送权限"
                
                elif "Could not read from remote repository" in error_msg:
                    error_msg += "\n\n请检查：\n" \
                                "1. SSH私钥是否正确（不带.pub后缀）\n" \
                                "2. 公钥是否已添加到远程仓库平台\n" \
                                "3. SSH agent是否正在运行"
                
                self.root.after(0, lambda: messagebox.showerror("推送失败", error_msg))
        
        thread = threading.Thread(target=push_thread)
        thread.daemon = True
        thread.start()
    
    def pull_changes(self):
        """从远程仓库拉取更新"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        self.log("开始从远程仓库拉取...", level="INFO")
        
        def pull_thread():
            try:
                repo = Repo(self.current_repo_path)
                platform = self.get_repo_platform(self.current_repo_path)
                config = self.platform_config.get_config(platform)
                
                if not repo.remotes:
                    self.root.after(0, lambda: messagebox.showwarning("警告", "没有配置远程仓库"))
                    return
                
                self.clear_proxy_env()
                
                original_url = repo.remotes[0].url
                
                pull_url = self.process_remote_url(original_url, platform, config)
                if self.is_ssh_url(pull_url):
                    if not self.setup_git_ssh(config):
                        self.root.after(0, lambda: messagebox.showerror("错误", "SSH密钥配置失败"))
                        return
                
                if pull_url != original_url:
                    repo.remotes[0].set_url(pull_url)
                
                try:
                    pull_info = repo.remotes[0].pull()
                    self.log("拉取成功！", level="SUCCESS")
                    self.root.after(0, lambda: messagebox.showinfo("成功", "拉取成功！"))
                    self.root.after(0, self.refresh_changes)
                finally:
                    if pull_url != original_url:
                        repo.remotes[0].set_url(original_url)
                    self.clear_proxy_env()
                
            except Exception as e:
                self.log(f"拉取失败: {str(e)}", level="ERROR")
                self.root.after(0, lambda: messagebox.showerror("错误", f"拉取失败：\n{str(e)}"))
        
        thread = threading.Thread(target=pull_thread)
        thread.daemon = True
        thread.start()
    
    def push_and_sync(self):
        """推送并同步到从仓库"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        repo_name = os.path.basename(self.current_repo_path)
        role = self.sync_manager.get_repo_role(repo_name)
        
        if role != 'main':
            self.push_changes()
            return
        
        subordinates = self.sync_manager.get_subordinates(repo_name)
        if not subordinates:
            self.push_changes()
            return
        
        self.log(f"开始同步推送: {repo_name} -> {', '.join(subordinates)}", level="SYNC")
        
        def sync_thread():
            try:
                self.log(f"[1/3] 推送主仓库: {repo_name}", level="SYNC")
                main_repo = Repo(self.current_repo_path)
                platform = self.get_repo_platform(self.current_repo_path)
                config = self.platform_config.get_config(platform)
                
                self.clear_proxy_env()
                
                if main_repo.remotes:
                    original_url = main_repo.remotes[0].url
                    
                    push_url = self.process_remote_url(original_url, platform, config)
                    if self.is_ssh_url(push_url):
                        self.setup_git_ssh(config)
                    
                    if push_url != original_url:
                        main_repo.remotes[0].set_url(push_url)
                    
                    try:
                        main_repo.remotes[0].push()
                        self.log(f"主仓库 {repo_name} 推送成功", level="SUCCESS")
                    finally:
                        if push_url != original_url:
                            main_repo.remotes[0].set_url(original_url)
                
                self.log(f"[2/3] 同步文件到从仓库", level="SYNC")
                
                for sub_repo_name in subordinates:
                    sub_repo_path = os.path.join(self.repos_folder, sub_repo_name)
                    if not os.path.exists(sub_repo_path):
                        self.log(f"从仓库 {sub_repo_name} 不存在，跳过", level="WARNING")
                        continue
                    
                    try:
                        self.sync_files(self.current_repo_path, sub_repo_path)
                        self.log(f"文件同步到 {sub_repo_name} 完成", level="SUCCESS")
                        
                        sub_repo = Repo(sub_repo_path)
                        
                        sub_repo.git.add('.')
                        
                        if sub_repo.is_dirty() or sub_repo.untracked_files:
                            commit_msg = f"Sync from main repo: {repo_name}"
                            sub_platform = self.get_repo_platform(sub_repo_path)
                            sub_config = self.platform_config.get_config(sub_platform)
                            
                            if sub_config.get('username') and sub_config.get('email'):
                                with sub_repo.config_writer() as git_config:
                                    git_config.set_value('user', 'name', sub_config['username'])
                                    git_config.set_value('user', 'email', sub_config['email'])
                            
                            sub_repo.index.commit(commit_msg)
                            
                            if sub_repo.remotes:
                                self.clear_proxy_env()
                                
                                sub_original_url = sub_repo.remotes[0].url
                                
                                sub_push_url = self.process_remote_url(sub_original_url, sub_platform, sub_config)
                                if self.is_ssh_url(sub_push_url):
                                    self.setup_git_ssh(sub_config)
                                
                                if sub_push_url != sub_original_url:
                                    sub_repo.remotes[0].set_url(sub_push_url)
                                
                                try:
                                    sub_repo.remotes[0].push()
                                    self.log(f"从仓库 {sub_repo_name} 推送成功", level="SUCCESS")
                                finally:
                                    if sub_push_url != sub_original_url:
                                        sub_repo.remotes[0].set_url(sub_original_url)
                            else:
                                self.log(f"从仓库 {sub_repo_name} 没有远程仓库，跳过推送", level="WARNING")
                        else:
                            self.log(f"从仓库 {sub_repo_name} 没有更改，跳过", level="INFO")
                    
                    except Exception as e:
                        self.log(f"同步到 {sub_repo_name} 失败: {str(e)}", level="ERROR")
                
                self.clear_proxy_env()
                
                self.log(f"[3/3] 同步完成！", level="SYNC")
                self.root.after(0, lambda: messagebox.showinfo("成功", f"同步推送完成！\n主仓库: {repo_name}\n从仓库: {', '.join(subordinates)}"))
                
            except Exception as e:
                self.log(f"同步推送失败: {str(e)}", level="ERROR")
                self.root.after(0, lambda: messagebox.showerror("错误", f"同步推送失败：\n{str(e)}"))
            finally:
                self.clear_proxy_env()
        
        thread = threading.Thread(target=sync_thread)
        thread.daemon = True
        thread.start()
    
    def sync_files(self, source_path, dest_path):
        """同步文件从源到目标（排除.git目录）"""
        for root, dirs, files in os.walk(source_path):
            if '.git' in dirs:
                dirs.remove('.git')
            
            rel_path = os.path.relpath(root, source_path)
            dest_root = os.path.join(dest_path, rel_path) if rel_path != '.' else dest_path
            
            os.makedirs(dest_root, exist_ok=True)
            
            for file in files:
                source_file = os.path.join(root, file)
                dest_file = os.path.join(dest_root, file)
                shutil.copy2(source_file, dest_file)
    
    def clone_repository(self):
        """克隆新仓库"""
        if not self.repos_folder:
            messagebox.showwarning("警告", "请先选择仓库文件夹")
            return
        
        dialog = tk.Toplevel(self.root)
        dialog.title("克隆仓库")
        dialog.geometry("850x600")
        dialog.transient(self.root)
        dialog.grab_set()
        
        self.center_window(dialog)
        
        main_frame = ttk.Frame(dialog, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 平台选择
        platform_frame = ttk.Frame(main_frame)
        platform_frame.pack(fill=tk.X, pady=(0, 15))
        
        ttk.Label(platform_frame, text="选择平台:", font=('Microsoft YaHei', 10, 'bold')).pack(side=tk.LEFT, padx=(0, 10))
        platform_var = tk.StringVar(value="GitHub")
        platform_combo = ttk.Combobox(platform_frame, textvariable=platform_var, 
                                      values=PlatformConfig.PLATFORMS, 
                                      state='readonly',
                                      width=20,
                                      font=('Microsoft YaHei', 10))
        platform_combo.pack(side=tk.LEFT)
        
        # URL输入
        url_frame = ttk.Frame(main_frame)
        url_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(url_frame, text="仓库URL:", font=('Microsoft YaHei', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        url_var = tk.StringVar()
        url_entry = ttk.Entry(url_frame, textvariable=url_var, font=('Microsoft YaHei', 10))
        url_entry.pack(fill=tk.X)
        
        url_type_var = tk.StringVar(value="")
        url_type_label = ttk.Label(url_frame, textvariable=url_type_var, foreground='blue')
        url_type_label.pack(anchor=tk.W, pady=(5, 0))
        
        def check_url_type(*args):
            url = url_var.get()
            if self.is_ssh_url(url):
                url_type_var.set("SSH协议 (将使用SSH密钥认证)")
            elif self.is_https_url(url):
                url_type_var.set("HTTPS协议 (将使用用户名/Token认证)")
            else:
                url_type_var.set("")
        
        url_var.trace('w', check_url_type)
        
        # 本地名称
        name_frame = ttk.Frame(main_frame)
        name_frame.pack(fill=tk.X, pady=(10, 15))
        
        ttk.Label(name_frame, text="本地名称:", font=('Microsoft YaHei', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
        name_var = tk.StringVar()
        name_entry = ttk.Entry(name_frame, textvariable=name_var, font=('Microsoft YaHei', 10))
        name_entry.pack(fill=tk.X)
        
        def extract_repo_name(*args):
            url = url_var.get()
            if url:
                name = url.rstrip('/').split('/')[-1]
                if name.endswith('.git'):
                    name = name[:-4]
                name_var.set(name)
        
        url_var.trace('w', extract_repo_name)
        
        # 克隆选项
        options_frame = ttk.LabelFrame(main_frame, text="克隆选项", padding="15")
        options_frame.pack(fill=tk.X, pady=15)
        
        depth_var = tk.IntVar(value=0)
        ttk.Checkbutton(options_frame, text="浅克隆（只克隆最近的历史）", 
                       variable=depth_var,
                       style='TCheckbutton').pack(anchor=tk.W)
        
        branch_frame = ttk.Frame(options_frame)
        branch_frame.pack(fill=tk.X, pady=(10, 0))
        
        ttk.Label(branch_frame, text="指定分支（可选）:").pack(side=tk.LEFT, padx=(0, 10))
        branch_var = tk.StringVar()
        ttk.Entry(branch_frame, textvariable=branch_var, width=30).pack(side=tk.LEFT)
        
        # 进度显示
        progress_frame = ttk.Frame(main_frame)
        progress_frame.pack(fill=tk.X, pady=15)
        
        progress_var = tk.StringVar(value="准备克隆...")
        ttk.Label(progress_frame, textvariable=progress_var, font=('Microsoft YaHei', 10)).pack()
        
        progress_bar = ttk.Progressbar(progress_frame, mode='indeterminate')
        progress_bar.pack(fill=tk.X, pady=10)
        
        def do_clone():
            url = url_var.get()
            name = name_var.get()
            
            if not url:
                messagebox.showwarning("警告", "请输入仓库URL")
                return
            
            if not name:
                messagebox.showwarning("警告", "请输入本地名称")
                return
            
            repo_path = os.path.join(self.repos_folder, name)
            if os.path.exists(repo_path):
                messagebox.showwarning("警告", f"文件夹 {name} 已存在")
                return
            
            progress_bar.start()
            progress_var.set(f"正在克隆 {name}...")
            
            def clone_thread():
                try:
                    platform = platform_var.get()
                    config = self.platform_config.get_config(platform)
                    
                    self.clear_proxy_env()
                    
                    clone_url = self.process_remote_url(url, platform, config)
                    if self.is_ssh_url(clone_url):
                        self.setup_git_ssh(config)
                        self.log("使用SSH协议克隆", level="INFO")
                    elif self.is_https_url(clone_url):
                        self.log("使用HTTPS协议克隆", level="INFO")
                    
                    kwargs = {}
                    if depth_var.get():
                        kwargs['depth'] = 1
                    if branch_var.get():
                        kwargs['branch'] = branch_var.get()
                    
                    repo = Repo.clone_from(clone_url, repo_path, **kwargs)
                    
                    if config.get('username') and config.get('email'):
                        with repo.config_writer() as git_config:
                            git_config.set_value('user', 'name', config['username'])
                            git_config.set_value('user', 'email', config['email'])
                    
                    if clone_url != url and repo.remotes:
                        repo.remotes[0].set_url(url)
                    
                    self.root.after(0, lambda: progress_bar.stop())
                    self.root.after(0, lambda: progress_var.set("克隆完成！"))
                    self.root.after(0, lambda: self.log(f"成功克隆仓库: {name}", level="SUCCESS"))
                    self.root.after(0, self.refresh_repo_list)
                    self.root.after(0, lambda: messagebox.showinfo("成功", f"仓库 {name} 克隆成功！"))
                    self.root.after(0, dialog.destroy)
                    
                except Exception as e:
                    self.root.after(0, lambda: progress_bar.stop())
                    self.root.after(0, lambda: progress_var.set("克隆失败"))
                    self.root.after(0, lambda: self.log(f"克隆失败: {str(e)}", level="ERROR"))
                    self.root.after(0, lambda: messagebox.showerror("克隆失败", str(e)))
                finally:
                    self.clear_proxy_env()
            
            thread = threading.Thread(target=clone_thread)
            thread.daemon = True
            thread.start()
        
        # 按钮
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(pady=20)
        
        ttk.Button(button_frame, text="开始克隆", command=do_clone, 
                  style='Action.TButton').pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=5)
    
    def build_auth_url(self, url, platform, config):
        """构建带认证的URL (仅用于HTTPS)"""
        if not self.is_https_url(url):
            return url
        
        if config.get('auth_type') == 'ssh':
            return url
        
        if not config.get('password'):
            return url
        
        parsed = urlparse(url)
        
        if platform == 'GitHub':
            auth = config['password']
            netloc = f"{auth}@{parsed.netloc}"
        else:
            if config.get('username'):
                auth = f"{config['username']}:{config['password']}"
                netloc = f"{auth}@{parsed.netloc}"
            else:
                netloc = parsed.netloc
        
        return urlunparse((parsed.scheme, netloc, parsed.path, 
                          parsed.params, parsed.query, parsed.fragment))
    
    def refresh_repo_list(self):
        """刷新仓库列表"""
        self.repo_listbox.delete(0, tk.END)
        
        if not self.repos_folder:
            self.log("请先选择仓库文件夹", level="WARNING")
            return
        
        if not os.path.exists(self.repos_folder):
            self.log(f"仓库文件夹不存在: {self.repos_folder}", level="ERROR")
            return
        
        repo_count = 0
        for item in sorted(os.listdir(self.repos_folder)):
            item_path = os.path.join(self.repos_folder, item)
            if os.path.isdir(item_path):
                git_path = os.path.join(item_path, '.git')
                if os.path.exists(git_path):
                    platform = self.get_repo_platform(item_path)
                    role = self.sync_manager.get_repo_role(item)
                    
                    role_mark = ""
                    if role == 'main':
                        role_mark = " [主]"
                    elif role == 'subordinate':
                        role_mark = " [从]"
                    
                    display_name = f"{item} [{platform}]{role_mark}"
                    self.repo_listbox.insert(tk.END, display_name)
                    repo_count += 1
                    
                    self.repos_info[item] = {
                        'path': item_path,
                        'platform': platform,
                        'role': role
                    }
        
        self.log(f"已加载 {repo_count} 个仓库", level="SUCCESS")
    
    def get_repo_platform(self, repo_path):
        """从远程URL推断平台"""
        try:
            repo = Repo(repo_path)
            if repo.remotes:
                url = repo.remotes[0].url.lower()
                if 'github.com' in url:
                    return 'GitHub'
                elif 'gitee.com' in url:
                    return 'Gitee'
                elif 'gitcode' in url:
                    return 'GitCode'
                elif 'gitlab' in url:
                    return 'GitLab'
        except:
            pass
        return '其他'
    
    def open_sync_settings(self):
        """打开同步配置窗口"""
        if not self.repos_folder:
            messagebox.showwarning("警告", "请先选择仓库文件夹")
            return
        
        dialog = tk.Toplevel(self.root)
        dialog.title("同步配置")
        dialog.geometry("950x750")
        dialog.transient(self.root)
        dialog.grab_set()
        
        self.center_window(dialog)
        
        main_frame = ttk.Frame(dialog, padding="25")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(main_frame, text="仓库同步配置", 
                 style='Title.TLabel').pack(pady=(0, 10))
        
        info_text = "配置主从仓库同步关系，推送主仓库时会自动同步到从仓库"
        ttk.Label(main_frame, text=info_text, foreground='gray').pack(pady=(0, 15))
        
        group_frame = ttk.LabelFrame(main_frame, text="同步组列表", padding="15")
        group_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        columns = ('组ID', '主仓库', '从仓库')
        tree = ttk.Treeview(group_frame, columns=columns, show='tree headings', height=12)
        tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        tree.heading('#0', text='')
        tree.column('#0', width=0, stretch=tk.NO)
        tree.heading('组ID', text='组ID')
        tree.column('组ID', width=120)
        tree.heading('主仓库', text='主仓库')
        tree.column('主仓库', width=250)
        tree.heading('从仓库', text='从仓库')
        tree.column('从仓库', width=400)
        
        scrollbar = ttk.Scrollbar(group_frame, orient=tk.VERTICAL, command=tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        tree.configure(yscrollcommand=scrollbar.set)
        
        def load_sync_groups():
            tree.delete(*tree.get_children())
            for group_id, group in self.sync_manager.sync_groups.items():
                subordinates = ', '.join(group['subordinates'])
                tree.insert('', 'end', values=(group_id, group['main'], subordinates))
        
        load_sync_groups()
        
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=15)
        
        def create_sync_group():
            create_dialog = tk.Toplevel(dialog)
            create_dialog.title("创建同步组")
            create_dialog.geometry("750x550")
            create_dialog.transient(dialog)
            create_dialog.grab_set()
            
            self.center_window(create_dialog)
            
            frame = ttk.Frame(create_dialog, padding="25")
            frame.pack(fill=tk.BOTH, expand=True)
            
            repos = []
            if os.path.exists(self.repos_folder):
                for item in sorted(os.listdir(self.repos_folder)):
                    item_path = os.path.join(self.repos_folder, item)
                    if os.path.isdir(item_path) and os.path.exists(os.path.join(item_path, '.git')):
                        if item not in self.sync_manager.repo_to_group:
                            repos.append(item)
            
            if not repos:
                messagebox.showinfo("提示", "没有可用的仓库或所有仓库已配置同步")
                create_dialog.destroy()
                return
            
            ttk.Label(frame, text="选择主仓库:", font=('Microsoft YaHei', 11, 'bold')).pack(anchor=tk.W, pady=(0, 10))
            
            main_var = tk.StringVar()
            main_combo = ttk.Combobox(frame, textvariable=main_var, values=repos, 
                                     state='readonly', width=45,
                                     font=('Microsoft YaHei', 10))
            main_combo.pack(fill=tk.X, pady=(0, 20))
            main_combo.current(0)
            
            ttk.Label(frame, text="选择从仓库（可多选）:", 
                     font=('Microsoft YaHei', 11, 'bold')).pack(anchor=tk.W, pady=(0, 10))
            
            sub_frame = ttk.Frame(frame)
            sub_frame.pack(fill=tk.BOTH, expand=True)
            
            sub_listbox = tk.Listbox(sub_frame, selectmode=tk.MULTIPLE, height=12,
                                    font=('Microsoft YaHei', 10))
            sub_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
            
            sub_scrollbar = ttk.Scrollbar(sub_frame, orient=tk.VERTICAL, command=sub_listbox.yview)
            sub_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
            sub_listbox.configure(yscrollcommand=sub_scrollbar.set)
            
            def update_sub_list(*args):
                sub_listbox.delete(0, tk.END)
                main_repo = main_var.get()
                for repo in repos:
                    if repo != main_repo:
                        sub_listbox.insert(tk.END, repo)
            
            main_var.trace('w', update_sub_list)
            update_sub_list()
            
            def do_create():
                main_repo = main_var.get()
                selected_indices = sub_listbox.curselection()
                
                if not selected_indices:
                    messagebox.showwarning("警告", "请选择至少一个从仓库")
                    return
                
                subordinates = [sub_listbox.get(i) for i in selected_indices]
                
                group_id = self.sync_manager.create_sync_group(main_repo, subordinates)
                self.save_config()
                
                self.log(f"创建同步组: {main_repo} -> {', '.join(subordinates)}", level="SYNC")
                load_sync_groups()
                create_dialog.destroy()
                messagebox.showinfo("成功", f"同步组创建成功！\n主仓库: {main_repo}\n从仓库: {', '.join(subordinates)}")
            
            btn_frame = ttk.Frame(frame)
            btn_frame.pack(pady=25)
            
            ttk.Button(btn_frame, text="创建", command=do_create,
                      style='Action.TButton').pack(side=tk.LEFT, padx=5)
            ttk.Button(btn_frame, text="取消", command=create_dialog.destroy).pack(side=tk.LEFT, padx=5)
        
        def delete_sync_group():
            selection = tree.selection()
            if not selection:
                messagebox.showwarning("警告", "请选择要删除的同步组")
                return
            
            item = tree.item(selection[0])
            group_id = item['values'][0]
            
            if messagebox.askyesno("确认", f"确定要删除同步组 {group_id} 吗？"):
                self.sync_manager.remove_sync_group(group_id)
                self.save_config()
                load_sync_groups()
                self.log(f"已删除同步组: {group_id}", level="SYNC")
        
        ttk.Button(button_frame, text="➕ 创建同步组", command=create_sync_group,
                  style='Action.TButton').pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="❌ 删除同步组", command=delete_sync_group,
                  style='Action.TButton').pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="关闭", command=dialog.destroy).pack(side=tk.RIGHT, padx=5)
    
    def open_platform_settings(self):
        """打开平台配置窗口"""
        settings_window = tk.Toplevel(self.root)
        settings_window.title("平台配置")
        settings_window.geometry("950x900")
        settings_window.transient(self.root)
        settings_window.grab_set()
        
        self.center_window(settings_window)
        
        notebook = ttk.Notebook(settings_window)
        notebook.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
        
        for platform in PlatformConfig.PLATFORMS:
            self.create_platform_tab(notebook, platform)
        
        button_frame = ttk.Frame(settings_window)
        button_frame.pack(pady=15)
        
        ttk.Button(button_frame, text="💾 保存所有配置", 
                  command=lambda: self.save_platform_settings(settings_window),
                  style='Action.TButton').pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=settings_window.destroy).pack(side=tk.LEFT, padx=5)
    
    def create_platform_tab(self, notebook, platform):
        """创建平台配置标签页 - 优化SSH密钥选择"""
        frame = ttk.Frame(notebook)
        notebook.add(frame, text=platform)
        
        config = self.platform_config.get_config(platform)
        
        vars = {}
        
        # 添加内边距
        inner_frame = ttk.Frame(frame)
        inner_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # 基本信息
        basic_frame = ttk.LabelFrame(inner_frame, text="基本信息", padding="15")
        basic_frame.pack(fill=tk.X, pady=(0, 15))
        
        ttk.Label(basic_frame, text="用户名:", font=('Microsoft YaHei', 10)).grid(row=0, column=0, sticky=tk.W, pady=8)
        vars['username'] = tk.StringVar(value=config.get('username', ''))
        ttk.Entry(basic_frame, textvariable=vars['username'], width=35,
                 font=('Microsoft YaHei', 10)).grid(row=0, column=1, pady=8)
        
        ttk.Label(basic_frame, text="邮箱:", font=('Microsoft YaHei', 10)).grid(row=1, column=0, sticky=tk.W, pady=8)
        vars['email'] = tk.StringVar(value=config.get('email', ''))
        ttk.Entry(basic_frame, textvariable=vars['email'], width=35,
                 font=('Microsoft YaHei', 10)).grid(row=1, column=1, pady=8)
        
        # 认证方式
        auth_frame = ttk.LabelFrame(inner_frame, text="认证方式", padding="15")
        auth_frame.pack(fill=tk.X, pady=(0, 15))
        
        vars['auth_type'] = tk.StringVar(value=config.get('auth_type', 'password'))
        
        def on_auth_change():
            auth_type = vars['auth_type'].get()
            if auth_type == 'password':
                password_frame.pack(fill=tk.X, pady=10)
                ssh_frame.pack_forget()
            else:
                ssh_frame.pack(fill=tk.X, pady=10)
                password_frame.pack_forget()
        
        radio_frame = ttk.Frame(auth_frame)
        radio_frame.pack(fill=tk.X)
        
        ttk.Radiobutton(radio_frame, text="账号密码/Token", variable=vars['auth_type'], 
                       value="password", command=on_auth_change).pack(side=tk.LEFT, padx=15)
        ttk.Radiobutton(radio_frame, text="SSH密钥", variable=vars['auth_type'], 
                       value="ssh", command=on_auth_change).pack(side=tk.LEFT, padx=15)
        
        # 密码认证
        password_frame = ttk.Frame(auth_frame)
        ttk.Label(password_frame, text="密码/Token:", font=('Microsoft YaHei', 10)).pack(side=tk.LEFT, padx=(30, 10))
        vars['password'] = tk.StringVar(value=config.get('password', ''))
        ttk.Entry(password_frame, textvariable=vars['password'], width=35, show="*",
                 font=('Microsoft YaHei', 10)).pack(side=tk.LEFT)
        
        # SSH认证
        ssh_frame = ttk.Frame(auth_frame)
        ttk.Label(ssh_frame, text="SSH私钥路径:", font=('Microsoft YaHei', 10)).pack(side=tk.LEFT, padx=(30, 10))
        
        # 自动修正公钥为私钥
        ssh_key_value = config.get('ssh_key_path', '')
        if ssh_key_value.endswith('.pub'):
            ssh_key_value = ssh_key_value[:-4]
        
        vars['ssh_key_path'] = tk.StringVar(value=ssh_key_value)
        ttk.Entry(ssh_frame, textvariable=vars['ssh_key_path'], width=28,
                 font=('Microsoft YaHei', 10)).pack(side=tk.LEFT)
        ttk.Button(ssh_frame, text="浏览", 
                  command=lambda: self.browse_ssh_key(vars['ssh_key_path'])).pack(side=tk.LEFT, padx=5)
        
        # SSH说明
        ssh_note = ttk.Label(auth_frame, 
                           text="⚠️ 注意：请选择SSH私钥文件（如id_ed25519），不要选择公钥文件（.pub后缀）", 
                           foreground='#FF8C00', font=('Microsoft YaHei', 9))
        
        on_auth_change()
        
        if vars['auth_type'].get() == 'ssh':
            ssh_note.pack(pady=5)
        
        # 代理设置
        proxy_frame = ttk.LabelFrame(inner_frame, text="代理设置 (仅HTTPS)", padding="15")
        proxy_frame.pack(fill=tk.X, pady=(0, 15))
        
        vars['use_proxy'] = tk.BooleanVar(value=config.get('use_proxy', platform == 'GitHub'))
        proxy_check = ttk.Checkbutton(proxy_frame, text="使用代理（仅对HTTPS有效，SSH不使用代理）", 
                                      variable=vars['use_proxy'])
        proxy_check.pack(anchor=tk.W)
        
        proxy_input_frame = ttk.Frame(proxy_frame)
        proxy_input_frame.pack(fill=tk.X, pady=10)
        
        ttk.Label(proxy_input_frame, text="代理地址:", font=('Microsoft YaHei', 10)).pack(side=tk.LEFT, padx=(30, 10))
        vars['proxy_url'] = tk.StringVar(value=config.get('proxy_url', PlatformConfig.DEFAULT_PROXIES[platform]))
        proxy_entry = ttk.Entry(proxy_input_frame, textvariable=vars['proxy_url'], width=45,
                               font=('Microsoft YaHei', 10))
        proxy_entry.pack(side=tk.LEFT)
        
        def toggle_proxy():
            if vars['use_proxy'].get():
                proxy_entry.config(state='normal')
            else:
                proxy_entry.config(state='disabled')
        
        proxy_check.config(command=toggle_proxy)
        toggle_proxy()
        
        # 平台说明
        info_frame = ttk.Frame(inner_frame)
        info_frame.pack(fill=tk.X, pady=10)
        
        if platform == 'GitHub':
            info = "GitHub 认证方式：\n\n" \
                  "1. HTTPS: 使用Personal Access Token\n" \
                  "   获取: Settings → Developer settings → Personal access tokens\n\n" \
                  "2. SSH: 使用SSH密钥对（推荐）\n" \
                  "   生成: ssh-keygen -t ed25519 -C \"your_email@example.com\"\n" \
                  "   添加公钥到: Settings → SSH and GPG keys"
        elif platform == 'Gitee':
            info = "Gitee 可以使用密码或私人令牌\n\n" \
                  "获取令牌：设置 → 安全设置 → 私人令牌"
        elif platform == 'GitCode':
            info = "GitCode 使用账号密码或SSH密钥\n\n" \
                  "SSH密钥添加：设置 → SSH密钥"
        else:
            info = f"{platform} 平台的认证配置"
        
        ttk.Label(info_frame, text=info, foreground='#4169E1', justify=tk.LEFT,
                 font=('Microsoft YaHei', 9)).pack(anchor=tk.W)
        
        # 测试连接按钮
        ttk.Button(inner_frame, text="🔧 测试配置", 
                  command=lambda: self.test_platform_config(platform, vars),
                  style='Action.TButton').pack(pady=15)
        
        frame.vars = vars
    
    def browse_ssh_key(self, var):
        """浏览SSH密钥文件 - 自动过滤公钥"""
        filename = filedialog.askopenfilename(
            title="选择SSH私钥文件（不要选择.pub文件）",
            initialdir=Path.home() / ".ssh",
            filetypes=[
                ("SSH私钥", "id_*"),
                ("ED25519密钥", "id_ed25519"),
                ("RSA密钥", "id_rsa"),
                ("所有文件", "*.*")
            ]
        )
        if filename:
            # 如果用户错误选择了公钥，自动修正
            if filename.endswith('.pub'):
                filename = filename[:-4]
                messagebox.showinfo("提示", f"已自动修正为私钥文件：\n{filename}")
            var.set(filename)
    
    def save_platform_settings(self, window):
        """保存平台配置 - 优化SSH密钥处理"""
        try:
            notebook = window.winfo_children()[0]
            for i, platform in enumerate(PlatformConfig.PLATFORMS):
                frame = notebook.winfo_children()[i]
                if hasattr(frame, 'vars'):
                    vars = frame.vars
                    
                    # 自动修正SSH密钥路径
                    ssh_key = vars.get('ssh_key_path', tk.StringVar()).get()
                    if ssh_key.endswith('.pub'):
                        ssh_key = ssh_key[:-4]
                    
                    config = {
                        'username': vars['username'].get(),
                        'email': vars['email'].get(),
                        'auth_type': vars['auth_type'].get(),
                        'password': vars.get('password', tk.StringVar()).get(),
                        'ssh_key_path': ssh_key,
                        'use_proxy': vars.get('use_proxy', tk.BooleanVar()).get(),
                        'proxy_url': vars.get('proxy_url', tk.StringVar()).get()
                    }
                    self.platform_config.set_config(platform, config)
            
            self.save_config()
            self.log("平台配置已保存", level="SUCCESS")
            messagebox.showinfo("成功", "平台配置已保存")
            window.destroy()
        except Exception as e:
            self.log(f"保存配置失败: {str(e)}", level="ERROR")
            messagebox.showerror("错误", f"保存失败：{str(e)}")
    
    def test_platform_config(self, platform, vars):
        """测试平台配置 - 优化SSH测试"""
        self.log(f"正在测试 {platform} 平台配置...", level="INFO")
        
        config = {
            'username': vars['username'].get(),
            'password': vars.get('password', tk.StringVar()).get(),
            'auth_type': vars['auth_type'].get(),
            'ssh_key_path': vars.get('ssh_key_path', tk.StringVar()).get()
        }
        
        # 自动修正SSH密钥路径
        if config['ssh_key_path'].endswith('.pub'):
            config['ssh_key_path'] = config['ssh_key_path'][:-4]
            self.log("自动修正：使用私钥文件而非公钥", level="WARNING")
        
        if config['auth_type'] == 'password':
            if not config['username'] or not config['password']:
                self.log(f"{platform} 配置测试失败: 用户名或密码为空", level="ERROR")
                messagebox.showwarning("配置不完整", "请填写用户名和密码/Token")
                return
        else:
            if not config['ssh_key_path'] or not os.path.exists(config['ssh_key_path']):
                self.log(f"{platform} 配置测试失败: SSH私钥文件不存在", level="ERROR")
                messagebox.showwarning("配置错误", 
                                     f"SSH私钥文件不存在：\n{config['ssh_key_path']}\n\n" +
                                     "请确保选择私钥文件（不带.pub后缀）")
                return
            
            # 检查对应的公钥是否存在
            pub_key = config['ssh_key_path'] + '.pub'
            if os.path.exists(pub_key):
                self.log(f"检测到对应的公钥文件: {pub_key}", level="INFO")
                self.log("请确保该公钥已添加到远程Git平台", level="INFO")
        
        self.log(f"{platform} 配置验证通过", level="SUCCESS")
        messagebox.showinfo("测试结果", 
                          f"{platform} 配置验证通过！\n\n" +
                          (f"SSH私钥: {config['ssh_key_path']}\n" if config['auth_type'] == 'ssh' else "") +
                          "可以正常使用")
    
    def batch_operations(self):
        """批量操作"""
        dialog = tk.Toplevel(self.root)
        dialog.title("批量操作")
        dialog.geometry("850x650")
        dialog.transient(self.root)
        dialog.grab_set()
        
        self.center_window(dialog)
        
        main_frame = ttk.Frame(dialog, padding="25")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(main_frame, text="选择操作类型:", 
                 font=('Microsoft YaHei', 11, 'bold')).pack(pady=(0, 15))
        
        operation_var = tk.StringVar(value="commit")
        operations = [
            ("批量提交", "commit"),
            ("批量推送", "push"),
            ("批量拉取", "pull"),
            ("批量提交并推送", "commit_push")
        ]
        
        op_frame = ttk.Frame(main_frame)
        op_frame.pack(fill=tk.X, pady=(0, 20))
        
        for text, value in operations:
            ttk.Radiobutton(op_frame, text=text, variable=operation_var, 
                           value=value).pack(anchor=tk.W, padx=50, pady=3)
        
        ttk.Label(main_frame, text="选择仓库:", 
                 font=('Microsoft YaHei', 11, 'bold')).pack(pady=(0, 10))
        
        list_frame = ttk.Frame(main_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=20)
        
        repo_listbox = tk.Listbox(list_frame, selectmode=tk.MULTIPLE, height=10,
                                 font=('Microsoft YaHei', 10))
        repo_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        repo_listbox.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=repo_listbox.yview)
        
        repos = []
        if self.repos_folder and os.path.exists(self.repos_folder):
            for item in sorted(os.listdir(self.repos_folder)):
                item_path = os.path.join(self.repos_folder, item)
                if os.path.isdir(item_path) and os.path.exists(os.path.join(item_path, '.git')):
                    repos.append((item, item_path))
                    platform = self.get_repo_platform(item_path)
                    role = self.sync_manager.get_repo_role(item)
                    
                    role_mark = ""
                    if role == 'main':
                        role_mark = " [主]"
                    elif role == 'subordinate':
                        role_mark = " [从]"
                    
                    display = f"{item} [{platform}]{role_mark}"
                    repo_listbox.insert(tk.END, display)
        
        select_frame = ttk.Frame(main_frame)
        select_frame.pack(pady=10)
        ttk.Button(select_frame, text="全选", 
                  command=lambda: repo_listbox.select_set(0, tk.END)).pack(side=tk.LEFT, padx=5)
        ttk.Button(select_frame, text="取消全选", 
                  command=lambda: repo_listbox.select_clear(0, tk.END)).pack(side=tk.LEFT, padx=5)
        
        commit_frame = ttk.Frame(main_frame)
        commit_frame.pack(fill=tk.X, padx=20, pady=15)
        
        ttk.Label(commit_frame, text="提交信息:", font=('Microsoft YaHei', 10)).pack(anchor=tk.W)
        commit_text = tk.Text(commit_frame, width=50, height=3, font=('Microsoft YaHei', 10))
        commit_text.pack(fill=tk.X)
        commit_text.insert(1.0, f"Batch update: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
        
        def execute_batch():
            selected_indices = repo_listbox.curselection()
            if not selected_indices:
                messagebox.showwarning("警告", "请选择至少一个仓库")
                return
            
            operation = operation_var.get()
            commit_msg = commit_text.get(1.0, tk.END).strip() if 'commit' in operation else None
            
            if 'commit' in operation and not commit_msg:
                messagebox.showwarning("警告", "请输入提交信息")
                return
            
            results = {'success': 0, 'failed': 0, 'skipped': 0}
            
            for index in selected_indices:
                repo_name, repo_path = repos[index]
                
                try:
                    repo = Repo(repo_path)
                    platform = self.get_repo_platform(repo_path)
                    config = self.platform_config.get_config(platform)
                    
                    self.clear_proxy_env()
                    
                    if config.get('username') and config.get('email'):
                        with repo.config_writer() as git_config:
                            git_config.set_value('user', 'name', config['username'])
                            git_config.set_value('user', 'email', config['email'])
                    
                    if operation == 'commit' or operation == 'commit_push':
                        repo.git.add('.')
                        
                        if repo.is_dirty() or repo.untracked_files:
                            repo.index.commit(commit_msg)
                            self.log(f"[{repo_name}] 提交成功", level="SUCCESS")
                            results['success'] += 1
                        else:
                            self.log(f"[{repo_name}] 跳过（无更改）")
                            results['skipped'] += 1
                    
                    if operation == 'push' or operation == 'commit_push':
                        if repo.remotes:
                            original_url = repo.remotes[0].url
                            
                            push_url = self.process_remote_url(original_url, platform, config)
                            if self.is_ssh_url(push_url):
                                self.setup_git_ssh(config)
                            
                            if push_url != original_url:
                                repo.remotes[0].set_url(push_url)
                            
                            try:
                                repo.remotes[0].push()
                                self.log(f"[{repo_name}] 推送成功", level="SUCCESS")
                                if operation == 'push':
                                    results['success'] += 1
                            finally:
                                if push_url != original_url:
                                    repo.remotes[0].set_url(original_url)
                        else:
                            self.log(f"[{repo_name}] 跳过（无远程仓库）", level="WARNING")
                            if operation == 'push':
                                results['skipped'] += 1
                    
                    if operation == 'pull':
                        if repo.remotes:
                            original_url = repo.remotes[0].url
                            
                            pull_url = self.process_remote_url(original_url, platform, config)
                            if self.is_ssh_url(pull_url):
                                self.setup_git_ssh(config)
                            
                            if pull_url != original_url:
                                repo.remotes[0].set_url(pull_url)
                            
                            try:
                                repo.remotes[0].pull()
                                self.log(f"[{repo_name}] 拉取成功", level="SUCCESS")
                                results['success'] += 1
                            finally:
                                if pull_url != original_url:
                                    repo.remotes[0].set_url(original_url)
                        else:
                            self.log(f"[{repo_name}] 跳过（无远程仓库）", level="WARNING")
                            results['skipped'] += 1
                    
                except Exception as e:
                    self.log(f"[{repo_name}] 操作失败: {str(e)}", level="ERROR")
                    results['failed'] += 1
                finally:
                    self.clear_proxy_env()
            
            msg = f"批量操作完成！\n\n成功: {results['success']} 个\n失败: {results['failed']} 个\n跳过: {results['skipped']} 个"
            messagebox.showinfo("操作完成", msg)
            dialog.destroy()
        
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(pady=20)
        
        ttk.Button(button_frame, text="执行", command=execute_batch,
                  style='Action.TButton').pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=5)
    
    def select_repos_folder(self):
        """选择包含多个仓库的文件夹"""
        folder = filedialog.askdirectory(title="选择包含Git仓库的文件夹")
        if folder:
            self.repos_folder = folder
            self.save_config()
            self.refresh_repo_list()
            self.log(f"已选择仓库文件夹: {folder}", level="SUCCESS")
    
    def filter_repos(self, *args):
        """过滤仓库列表"""
        search_term = self.search_var.get().lower()
        self.repo_listbox.delete(0, tk.END)
        
        if not self.repos_folder or not os.path.exists(self.repos_folder):
            return
        
        for item in sorted(os.listdir(self.repos_folder)):
            if search_term and search_term not in item.lower():
                continue
                
            item_path = os.path.join(self.repos_folder, item)
            if os.path.isdir(item_path) and os.path.exists(os.path.join(item_path, '.git')):
                platform = self.get_repo_platform(item_path)
                role = self.sync_manager.get_repo_role(item)
                
                role_mark = ""
                if role == 'main':
                    role_mark = " [主]"
                elif role == 'subordinate':
                    role_mark = " [从]"
                
                display_name = f"{item} [{platform}]{role_mark}"
                self.repo_listbox.insert(tk.END, display_name)
    
    def refresh_changes(self):
        """刷新文件变更列表 - 修复空仓库处理"""
        if not self.current_repo_path:
            return
        
        try:
            repo = Repo(self.current_repo_path)
            self.changes_listbox.delete(0, tk.END)
            
            changes = []
            
            # 检查是否是空仓库
            try:
                repo.head.ref
                
                # 非空仓库，正常处理
                for item in repo.index.diff(None):
                    changes.append(f"M {item.a_path}")
                
                for item in repo.index.diff("HEAD"):
                    changes.append(f"A {item.a_path}")
                
                for item in repo.untracked_files:
                    changes.append(f"? {item}")
                
                for item in repo.index.diff(None).iter_change_type('D'):
                    changes.append(f"D {item.a_path}")
                    
            except:
                # 空仓库，只显示未跟踪的文件
                for item in repo.untracked_files:
                    changes.append(f"? {item}")
                
                if not changes:
                    self.changes_listbox.insert(tk.END, "（空仓库，等待首次提交）")
                    return
            
            if changes:
                for change in sorted(set(changes)):
                    self.changes_listbox.insert(tk.END, change)
                self.log(f"检测到 {len(changes)} 个文件变更")
            else:
                self.changes_listbox.insert(tk.END, "（没有检测到变更）")
                
        except Exception as e:
            # 忽略空仓库的特定错误
            if "did not resolve to an object" in str(e):
                self.changes_listbox.delete(0, tk.END)
                self.changes_listbox.insert(tk.END, "（空仓库，等待首次提交）")
            else:
                self.log(f"刷新变更失败: {str(e)}", level="ERROR")
    
    def add_files(self):
        """添加文件到暂存区"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        try:
            repo = Repo(self.current_repo_path)
            selection = self.changes_listbox.curselection()
            
            if not selection:
                repo.git.add('.')
                self.log("已添加所有文件到暂存区", level="SUCCESS")
            else:
                for index in selection:
                    file_info = self.changes_listbox.get(index)
                    if file_info and "没有检测到变更" not in file_info and "空仓库" not in file_info:
                        file_path = file_info.split(' ', 1)[1]
                        repo.index.add([file_path])
                        self.log(f"已添加文件: {file_path}")
            
            self.refresh_changes()
            
        except Exception as e:
            self.log(f"添加文件失败: {str(e)}", level="ERROR")
    
    def create_branch(self):
        """创建新分支"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        branch_name = simpledialog.askstring("创建分支", "请输入新分支名称:")
        if branch_name:
            try:
                repo = Repo(self.current_repo_path)
                
                # 检查是否是空仓库
                try:
                    repo.head.ref
                except:
                    messagebox.showwarning("警告", "空仓库不能创建分支，请先进行首次提交")
                    return
                
                new_branch = repo.create_head(branch_name)
                new_branch.checkout()
                self.branch_info_var.set(branch_name)
                self.log(f"创建并切换到分支: {branch_name}", level="SUCCESS")
                messagebox.showinfo("成功", f"已创建并切换到分支: {branch_name}")
            except Exception as e:
                self.log(f"创建分支失败: {str(e)}", level="ERROR")
                messagebox.showerror("错误", f"创建分支失败：\n{str(e)}")
    
    def switch_branch(self):
        """切换分支"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        try:
            repo = Repo(self.current_repo_path)
            
            # 检查是否是空仓库
            try:
                repo.head.ref
            except:
                messagebox.showinfo("提示", "空仓库没有分支可切换")
                return
            
            branches = [branch.name for branch in repo.heads]
            
            if not branches:
                messagebox.showinfo("提示", "没有其他分支")
                return
            
            dialog = tk.Toplevel(self.root)
            dialog.title("切换分支")
            dialog.geometry("550x350")
            dialog.transient(self.root)
            dialog.grab_set()
            
            self.center_window(dialog)
            
            main_frame = ttk.Frame(dialog, padding="25")
            main_frame.pack(fill=tk.BOTH, expand=True)
            
            ttk.Label(main_frame, text="选择要切换的分支:", 
                     font=('Microsoft YaHei', 11, 'bold')).pack(pady=(0, 15))
            
            branch_var = tk.StringVar(value=repo.active_branch.name)
            
            branch_frame = ttk.Frame(main_frame)
            branch_frame.pack(fill=tk.BOTH, expand=True)
            
            for branch in branches:
                ttk.Radiobutton(branch_frame, text=branch, variable=branch_var, 
                               value=branch).pack(anchor=tk.W, padx=30, pady=5)
            
            def do_switch():
                selected = branch_var.get()
                try:
                    repo.git.checkout(selected)
                    self.branch_info_var.set(selected)
                    self.log(f"已切换到分支: {selected}", level="SUCCESS")
                    self.refresh_changes()
                    dialog.destroy()
                    messagebox.showinfo("成功", f"已切换到分支: {selected}")
                except Exception as e:
                    messagebox.showerror("错误", f"切换失败：\n{str(e)}")
            
            ttk.Button(main_frame, text="切换", command=do_switch,
                      style='Action.TButton').pack(pady=20)
            
        except Exception as e:
            self.log(f"获取分支列表失败: {str(e)}", level="ERROR")
    
    def merge_branch(self):
        """合并分支"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        try:
            repo = Repo(self.current_repo_path)
            
            # 检查是否是空仓库
            try:
                current = repo.active_branch.name
            except:
                messagebox.showinfo("提示", "空仓库不能进行分支合并")
                return
            
            branches = [b.name for b in repo.heads if b.name != current]
            
            if not branches:
                messagebox.showinfo("提示", "没有其他分支可合并")
                return
            
            dialog = tk.Toplevel(self.root)
            dialog.title("合并分支")
            dialog.geometry("650x400")
            dialog.transient(self.root)
            dialog.grab_set()
            
            self.center_window(dialog)
            
            main_frame = ttk.Frame(dialog, padding="25")
            main_frame.pack(fill=tk.BOTH, expand=True)
            
            ttk.Label(main_frame, text=f"将分支合并到 {current}:", 
                     font=('Microsoft YaHei', 11, 'bold')).pack(pady=(0, 15))
            
            branch_var = tk.StringVar(value=branches[0] if branches else "")
            ttk.Combobox(main_frame, textvariable=branch_var, values=branches, 
                        state='readonly', width=40,
                        font=('Microsoft YaHei', 10)).pack(pady=15)
            
            def do_merge():
                source = branch_var.get()
                if source:
                    try:
                        repo.git.merge(source)
                        self.log(f"成功合并分支 {source} 到 {current}", level="SUCCESS")
                        self.refresh_changes()
                        dialog.destroy()
                        messagebox.showinfo("成功", f"已合并分支 {source} 到 {current}")
                    except Exception as e:
                        messagebox.showerror("错误", f"合并失败：\n{str(e)}")
            
            ttk.Button(main_frame, text="合并", command=do_merge,
                      style='Action.TButton').pack(pady=20)
            
        except Exception as e:
            self.log(f"合并分支失败: {str(e)}", level="ERROR")
    
    def view_log(self):
        """查看提交日志"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        try:
            repo = Repo(self.current_repo_path)
            
            log_window = tk.Toplevel(self.root)
            log_window.title(f"提交日志 - {os.path.basename(self.current_repo_path)}")
            log_window.geometry("1100x750")
            log_window.transient(self.root)
            
            self.center_window(log_window)
            
            main_frame = ttk.Frame(log_window, padding="15")
            main_frame.pack(fill=tk.BOTH, expand=True)
            
            log_text = scrolledtext.ScrolledText(main_frame, wrap=tk.WORD, 
                                                font=('Consolas', 10))
            log_text.pack(fill=tk.BOTH, expand=True)
            
            # 检查是否是空仓库
            try:
                commits = list(repo.iter_commits(max_count=50))
                
                for commit in commits:
                    log_entry = f"{'='*70}\n"
                    log_entry += f"提交: {commit.hexsha[:8]}\n"
                    log_entry += f"作者: {commit.author.name} <{commit.author.email}>\n"
                    log_entry += f"日期: {datetime.fromtimestamp(commit.committed_date).strftime('%Y-%m-%d %H:%M:%S')}\n"
                    log_entry += f"信息: {commit.message}\n"
                    log_text.insert(tk.END, log_entry)
            except:
                log_text.insert(tk.END, "空仓库，还没有提交记录")
            
            log_text.config(state='disabled')
            
            ttk.Button(main_frame, text="关闭", command=log_window.destroy).pack(pady=10)
            
        except Exception as e:
            self.log(f"查看日志失败: {str(e)}", level="ERROR")
            messagebox.showerror("错误", f"查看日志失败：\n{str(e)}")
    
    def view_diff(self):
        """查看差异"""
        if not self.current_repo_path:
            messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        try:
            repo = Repo(self.current_repo_path)
            
            # 检查是否是空仓库
            try:
                diff = repo.git.diff()
            except:
                messagebox.showinfo("提示", "空仓库没有差异可查看")
                return
            
            if not diff:
                messagebox.showinfo("提示", "没有检测到差异")
                return
            
            diff_window = tk.Toplevel(self.root)
            diff_window.title(f"文件差异 - {os.path.basename(self.current_repo_path)}")
            diff_window.geometry("1200x750")
            diff_window.transient(self.root)
            
            self.center_window(diff_window)
            
            main_frame = ttk.Frame(diff_window, padding="15")
            main_frame.pack(fill=tk.BOTH, expand=True)
            
            diff_text = scrolledtext.ScrolledText(main_frame, wrap=tk.NONE, 
                                                 font=('Consolas', 10))
            diff_text.pack(fill=tk.BOTH, expand=True)
            
            diff_text.tag_config("add", foreground="green", background="#e6ffed")
            diff_text.tag_config("remove", foreground="red", background="#ffeef0")
            diff_text.tag_config("header", foreground="blue", font=('Consolas', 10, 'bold'))
            
            for line in diff.split('\n'):
                if line.startswith('+'):
                    diff_text.insert(tk.END, line + '\n', 'add')
                elif line.startswith('-'):
                    diff_text.insert(tk.END, line + '\n', 'remove')
                elif line.startswith('@@'):
                    diff_text.insert(tk.END, line + '\n', 'header')
                else:
                    diff_text.insert(tk.END, line + '\n')
            
            diff_text.config(state='disabled')
            
            ttk.Button(main_frame, text="关闭", command=diff_window.destroy).pack(pady=10)
            
        except Exception as e:
            self.log(f"查看差异失败: {str(e)}", level="ERROR")
            messagebox.showerror("错误", f"查看差异失败：\n{str(e)}")
    
    def open_repo_folder(self, event=None):
        """打开仓库文件夹"""
        if not self.current_repo_path:
            if event:
                self.on_repo_select(None)
            else:
                messagebox.showwarning("警告", "请先选择一个仓库")
            return
        
        try:
            if sys.platform == 'win32':
                os.startfile(self.current_repo_path)
            elif sys.platform == 'darwin':
                subprocess.call(['open', self.current_repo_path])
            else:
                subprocess.call(['xdg-open', self.current_repo_path])
            
            self.log(f"已打开文件夹: {self.current_repo_path}")
        except Exception as e:
            self.log(f"打开文件夹失败: {str(e)}", level="ERROR")
    
    def show_help(self):
        """显示使用帮助"""
        help_window = tk.Toplevel(self.root)
        help_window.title("使用帮助")
        help_window.geometry("1100x800")
        help_window.transient(self.root)
        
        self.center_window(help_window)
        
        main_frame = ttk.Frame(help_window, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        help_text = scrolledtext.ScrolledText(main_frame, wrap=tk.WORD, 
                                             font=('Microsoft YaHei', 10),
                                             bg='#FFFFF0')
        help_text.pack(fill=tk.BOTH, expand=True)
        
        help_content = """
🌻 向日葵Git仓库管理 v2.4 (增强版) - 使用帮助

【版本更新】
v2.4 优化内容:
• 修复SSH认证问题（自动处理公钥/私钥）
• 优化空仓库处理，避免报错
• 美化界面样式，采用向日葵配色
• 改进错误提示和用户引导

【快速开始】
1. 点击"选择文件夹"，选择包含Git仓库的父文件夹
2. 点击"平台配置"，为每个平台设置认证信息
3. 点击"同步配置"，设置主从仓库关系（可选）
4. 从左侧列表选择仓库进行操作
5. 使用快速提交区域进行一键提交和推送

【SSH配置重点说明】⚠️
◆ SSH密钥文件选择
  - 必须选择私钥文件（如：id_ed25519）
  - 不要选择公钥文件（.pub后缀）
  - 系统会自动修正错误选择

◆ SSH密钥生成步骤
  1. 打开终端或命令行
  2. 执行：ssh-keygen -t ed25519 -C "your_email@example.com"
  3. 默认保存在：~/.ssh/id_ed25519（私钥）和 ~/.ssh/id_ed25519.pub（公钥）
  4. 将公钥内容添加到GitHub/GitCode等平台的SSH密钥设置中

【一键提交功能】
◆ 快速提交
  - 在提交信息框输入内容
  - 点击"一键提交"按钮
  - 自动添加所有文件并提交

◆ 提交并推送
  - 一次操作完成提交和推送
  - 适合频繁更新的场景

◆ 提交并同步
  - 提交主仓库并同步到从仓库
  - 适合多平台镜像管理

【平台配置说明】

◆ GitHub配置
  SSH方式（推荐）:
  - 选择"SSH密钥"认证
  - 选择私钥文件路径（不带.pub）
  - 无需配置代理
  - 将公钥添加到：Settings → SSH and GPG keys
  
  HTTPS方式:
  - 选择"账号密码/Token"认证
  - 使用Personal Access Token
  - 可配置gh-proxy.com代理

◆ Gitee配置  
  - 支持密码或私人令牌
  - 通常不需要代理
  - SSH和HTTPS都可用

◆ GitCode配置
  - 支持SSH密钥和账号密码
  - SSH密钥添加：设置 → SSH密钥
  - 选择私钥文件（不带.pub）

【空仓库处理】
- 系统会自动检测空仓库
- 空仓库显示"空仓库 (no commits)"
- 支持在空仓库中进行首次提交
- 空仓库不支持分支操作

【同步功能】
◆ 主从仓库同步
  - 配置一个主仓库和多个从仓库
  - 推送主仓库时自动同步到从仓库
  - 适合多平台镜像仓库管理

【批量操作】
- 批量提交: 一次提交多个仓库
- 批量推送: 推送多个仓库
- 批量拉取: 从远程更新多个仓库

【快捷操作】
- 双击仓库: 打开仓库文件夹
- 搜索框: 快速过滤仓库列表
- 不选文件直接"添加": 添加所有更改

【故障排查】
◆ SSH推送失败
  1. 检查是否选择了正确的私钥文件（不带.pub）
  2. 确认公钥已添加到远程平台
  3. 测试SSH连接：ssh -T git@github.com

◆ 空仓库报错
  - 系统已自动处理，不影响使用
  - 进行首次提交后即可正常使用所有功能

【安全提示】
- 认证信息保存在本地配置文件
- 建议使用SSH密钥而非密码
- Token需要定期更新
- 不要分享配置文件

版本: v2.4 (增强版)
作者: 向日葵
        """
        
        help_text.insert(1.0, help_content)
        help_text.config(state='disabled')
        
        ttk.Button(main_frame, text="关闭", command=help_window.destroy,
                  style='Action.TButton').pack(pady=15)
    
    def log(self, message, level="INFO"):
        """记录日志"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_entry = f"[{timestamp}] [{level}] {message}\n"
        
        if self.log_text:
            self.log_text.insert(tk.END, log_entry, level)
            self.log_text.see(tk.END)
            
            lines = int(self.log_text.index('end-1c').split('.')[0])
            if lines > 500:
                self.log_text.delete('1.0', '2.0')
        else:
            print(log_entry.strip())
    
    def load_config(self):
        """加载配置文件"""
        try:
            if self.config_file.exists():
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    
                    if 'platform_configs' in data:
                        self.platform_config.from_dict(data['platform_configs'])
                    
                    if 'sync_manager' in data:
                        self.sync_manager.from_dict(data['sync_manager'])
                    
                    self.repos_folder = data.get('repos_folder', None)
                    
                self.log("配置文件加载成功", level="SUCCESS")
        except Exception as e:
            self.log(f"加载配置失败: {str(e)}", level="ERROR")
    
    def save_config(self):
        """保存配置文件"""
        try:
            data = {
                'platform_configs': self.platform_config.to_dict(),
                'sync_manager': self.sync_manager.to_dict(),
                'repos_folder': self.repos_folder
            }
            
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
            
            self.log("配置已自动保存")
        except Exception as e:
            self.log(f"保存配置失败: {str(e)}", level="ERROR")
    
    def run(self):
        """运行应用"""
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.root.mainloop()
    
    def on_closing(self):
        """窗口关闭时的处理"""
        if messagebox.askokcancel("退出", "确定要退出向日葵Git仓库管理吗？"):
            self.save_config()
            self.root.destroy()

def main():
    """主函数"""
    print("=" * 60)
    print("🌻 向日葵Git仓库管理 v2.4 (增强版)")
    print("=" * 60)
    
    # 检查Python版本
    if sys.version_info < (3, 6):
        print("错误：需要Python 3.6或更高版本")
        input("按Enter键退出...")
        return
    
    # 检查Git是否安装
    try:
        subprocess.run(['git', '--version'], check=True, capture_output=True)
        print("✓ Git 已安装")
    except:
        print("✗ Git 未安装，请先安装Git")
        input("按Enter键退出...")
        return
    
    # 检查并安装GitPython
    try:
        import git
        print("✓ GitPython 已安装")
    except ImportError:
        print("正在安装 GitPython...")
        try:
            subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'GitPython'])
            print("✓ GitPython 安装成功")
        except:
            print("✗ GitPython 安装失败")
            print("请手动安装: pip install GitPython")
            input("按Enter键退出...")
            return
    
    print("\n正在启动图形界面...")
    
    # 启动应用
    try:
        app = GitRepoManager()
        app.run()
    except Exception as e:
        print(f"\n程序运行出错：{str(e)}")
        import traceback
        traceback.print_exc()
        input("\n按Enter键退出...")

if __name__ == "__main__":
    main()